package devops

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime"
	"time"

	"away.wei/minidevpos/global"
	"github.com/shirou/gopsutil/v3/process"
)

type SHELL struct {
	cmd        *exec.Cmd
	logFile    string
	scriptFile string // 脚本文件
	projectId  int    // 项目编号
}

var (
	isWindows      = runtime.GOOS == "windows"
	scriptRootPath = global.Abpath
)

func (s *SHELL) rmScriptFile() error {
	return os.Remove(s.scriptFile)
}

func (s *SHELL) New(logFile string, projectId int) {
	s.logFile = logFile
	s.projectId = projectId
	s.cmd = new(exec.Cmd)
}

func (s *SHELL) SetLogWrite(logger *log.Logger) {
	s.cmd.Stderr = logger.Writer()
	s.cmd.Stdout = logger.Writer()
}

func (s *SHELL) SetEnvs(envs map[string]string) {
	if s.cmd.Env == nil {
		s.cmd.Env = os.Environ()
	}
	for key, val := range envs {
		s.cmd.Env = append(s.cmd.Env, fmt.Sprintf("%s=%s", key, val))
	}
}

func (s *SHELL) SetDir(dir string) {
	s.cmd.Dir = dir
}

func (s *SHELL) Script(arg string) (err error) {
	filename := fmt.Sprint(time.Now().Unix())
	s.scriptFile = fmt.Sprintf("%s/%s.sh", scriptRootPath, filename)
	f, _ := os.OpenFile(s.scriptFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
	_, err = f.WriteString(arg)
	f.Close()
	if err != nil {
		return err
	}

	shell := fmt.Sprintf("sh -xe %s>> %s 2>&1", s.scriptFile, s.logFile)
	if isWindows {
		s.cmd.Path = "C:/Program Files/Git/bin/bash.exe"
	} else {
		s.cmd.Path = "/usr/bin/bash"
	}
	s.cmd.Args = append(s.cmd.Args, "--login", "-c", shell) // "--login", "-c", 一定要拆开写，别问为什么，shit!!
	return nil
}

func (s *SHELL) Run() (status int, err error) {
	err = s.cmd.Start()
	if err != nil {
		return status_fail, err
	}

	done := make(chan error, 1)
	go func() {
		done <- s.cmd.Wait()
	}()
	for {
		select {
		case <-done:
			s.rmScriptFile()
			return status_finished, nil
		case projectId, ok := <-global.DevOpsCancelChan:
			if !ok {
				continue
			}
			if projectId == s.projectId {
				p, err := process.NewProcess(int32(s.cmd.Process.Pid))
				if err != nil {
					return status_stop, err
				}

				cp, err := p.Children()
				if err != nil {
					p.Kill()
					return status_stop, err
				}
				for _, v := range cp {
					v.Kill()
				}
				p.Kill()
				s.rmScriptFile()
				return status_stop, nil
			}
			global.DevOpsCancelChan <- projectId
			// default:
			// close(global.DevOpsCancelChan)

		}
	}
}
